package com.color.admin.system.servie.impl;


import com.color.admin.auth.entity.LoginDto;
import com.color.admin.common.core.constant.Constant;
import com.color.admin.common.core.exception.BaseException;
import com.color.admin.common.core.page.PageRequest;
import com.color.admin.common.core.page.PageResponse;
import com.color.admin.common.core.utils.cache.RedisUtils;
import com.color.admin.common.core.utils.tools.QueryHelp;
import com.color.admin.common.core.utils.web.ServletUtils;
import com.color.admin.common.core.utils.web.TokenUtils;
import com.color.admin.common.security.entity.SecurityUser;
import com.color.admin.system.entity.SysDept;
import com.color.admin.system.entity.SysPost;
import com.color.admin.system.entity.SysRole;
import com.color.admin.system.entity.SysUser;
import com.color.admin.system.entity.dto.PwdDto;
import com.color.admin.system.entity.dto.RestPwd;
import com.color.admin.system.entity.dto.UserDto;
import com.color.admin.system.entity.query.UserQueryCriteria;
import com.color.admin.system.entity.vo.dept.DeptSmallVo;
import com.color.admin.system.entity.vo.post.PostSmallVo;
import com.color.admin.system.entity.vo.role.RoleSmallVo;
import com.color.admin.system.entity.vo.user.UserSmallVo;
import com.color.admin.system.entity.vo.user.UserVo;
import com.color.admin.system.repository.UserRepository;
import com.color.admin.system.servie.DeptService;
import com.color.admin.system.servie.MenuService;
import com.color.admin.system.servie.UserService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author HaiQiu
 * @date 2021/12/6
 */
@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserRepository userRepository;

    @Autowired
    private DeptService deptService;

    @Autowired
    private MenuService menuService;

    @Autowired
    private RedisUtils redisUtils;

    @Resource
    private AuthenticationManager authenticationManager;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void save(SysUser sysUser) {
        SysUser user = userRepository.findByUsername(sysUser.getUsername());
        if (!ObjectUtils.isEmpty(user)) {
            throw new BaseException("该用户名已存在");
        }
        if (!StringUtils.hasText(sysUser.getNickname())) {
            sysUser.setNickname(sysUser.getUsername());
        }
        if (!StringUtils.hasText(sysUser.getRealName())) {
            sysUser.setNickname(sysUser.getUsername());
        }
        sysUser.setCreateTime(new Date());
        sysUser.setUpdateTime(new Date());
        if (ObjectUtils.isEmpty(userRepository.save(sysUser))) {
            throw new BaseException(Constant.UPDATE_FAIL);
        }
    }


    @Override
    public Map<String, Object> login(LoginDto loginDto) {
        SysUser sysUser = userRepository.findByUsername(loginDto.getUsername());
        if (sysUser == null) {
            throw new BaseException("账户不存在");
        }
        if (!sysUser.getPassword().equals(loginDto.getPassword())) {
            throw new BaseException("密码错误");
        }
        if (!sysUser.getActive()){
            throw new BaseException("账户未激活");
        }
        if (sysUser.getStatus().equals(Constant.STATUS_DISABLE)){
            throw new BaseException("账户被冻结");
        }
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
                loginDto.getUsername(),loginDto.getPassword());
        // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
        Authentication authenticate = authenticationManager.authenticate(authenticationToken);
        SecurityUser details = (SecurityUser)authenticate.getPrincipal();
        redisUtils.set(Constant.USER_SECURITY + details.getUsername(),details);
        UserVo userVo = new UserVo();
        BeanUtils.copyProperties(sysUser, userVo);
        userVo.setDept(getDept(sysUser.getDept()));
        userVo.setRoles(getRoles(sysUser.getRoles()));
        Map<String, Object> map = new HashMap<>(6);
        map.put("token", TokenUtils.generateToken(sysUser.getId(), sysUser.getUsername()));
        map.put("userInfo", userVo);
        map.put("refreshToken", TokenUtils.generateToken(sysUser.getId(), sysUser.getUsername()));
        //登录完成执行缓存全局权限列表
//        menuService.authList(sysUser.getId());
        return map;
    }

    @Override
    public UserVo get(String userId) {
        SysUser user = userRepository.findById(userId).orElseGet(SysUser::new);
        UserVo userVo = new UserVo();
        BeanUtils.copyProperties(user, userVo);
        userVo.setDept(getDept(user.getDept()));
        userVo.setRoles(getRoles(user.getRoles()));
        userVo.setPosts(getPosts(user.getPosts()));
        return userVo;
    }

    @Override
    public PageResponse<UserVo> list(PageRequest<UserQueryCriteria> request) {
        //解析查询参数
        UserQueryCriteria criteria = request.getParams();
        //部门参数筛选
        if (!ObjectUtils.isEmpty(criteria)) {
            setQueryByDeptParams(criteria);
        }
        //排序
        Pageable pageable = org.springframework.data.domain.PageRequest.of(request.getNum(),
                request.getPageSize(), Sort.by("sort"));
        //数据库查询
        Page<SysUser> users = userRepository.findAll(((root, query, criteriaBuilder) ->
                QueryHelp.getPredicate(root, criteria, criteriaBuilder)), pageable);
        //实体转换
        Page<UserVo> userVos = getUserVos(users);
        //设置返回数据
        PageResponse<UserVo> response = new PageResponse<>();
        response.setTotalElements(userVos.getTotalElements());
        response.setPageIndex(request.getPageIndex());
        response.setPageSize(request.getPageSize());
        response.setData(userVos.getContent());
        return response;
    }

    /**
     * 数据集合转换
     *
     * @param users 原始集合
     * @return 转换后集合
     */
    private Page<UserVo> getUserVos(Page<SysUser> users) {
        Page<UserVo> userVos = users.map(sysUser -> {
            UserVo userVo = new UserVo();
            BeanUtils.copyProperties(sysUser, userVo);
            userVo.setDept(getDept(sysUser.getDept()));
            userVo.setRoles(getRoles(sysUser.getRoles()));
            userVo.setPosts(getPosts(sysUser.getPosts()));
            return userVo;
        });
        return userVos;
    }

    /**
     * 设置部门查询参数
     *
     * @param criteria 查询参数
     */
    private void setQueryByDeptParams(UserQueryCriteria criteria) {
        if (StringUtils.hasText(criteria.getDeptId())) {
            List<SysDept> depts = new ArrayList<>();
            if (criteria.getFindIsDeptChildren()) {
                depts = deptService.findAllByPid(criteria.getDeptId());
            } else {
                SysDept sysDept = new SysDept();
                sysDept.setId(criteria.getDeptId());
                depts.add(sysDept);
            }
            Set<String> deptIds = depts.stream().map(SysDept::getId).collect(Collectors.toSet());
            criteria.setDeptIds(deptIds);
        }
    }

    /**
     * 设置角色
     *
     * @param sysRoles 角色集合
     * @return 角色集合Small
     */
    private Set<RoleSmallVo> getRoles(Set<SysRole> sysRoles) {
        Set<RoleSmallVo> roleSmallVos = new HashSet<>();
        if (!CollectionUtils.isEmpty(sysRoles)) {
            sysRoles.forEach(sysRole -> {
                RoleSmallVo roleSmallVo = new RoleSmallVo();
                roleSmallVo.setId(sysRole.getId());
                roleSmallVo.setRoleName(sysRole.getRoleName());
                roleSmallVos.add(roleSmallVo);
            });
            return roleSmallVos;
        }
        return new HashSet<>();
    }

    /**
     * 设置职位
     *
     * @param sysPosts 职位集合
     * @return 职位集合Small
     */
    private Set<PostSmallVo> getPosts(Set<SysPost> sysPosts) {
        Set<PostSmallVo> postSmallVos = new HashSet<>();
        if (!CollectionUtils.isEmpty(sysPosts)) {
            sysPosts.forEach(sysPost -> {
                PostSmallVo roleSmallVo = new PostSmallVo();
                roleSmallVo.setId(sysPost.getId());
                roleSmallVo.setPostName(sysPost.getPostName());
                postSmallVos.add(roleSmallVo);
            });
            return postSmallVos;
        }
        return new HashSet<>();
    }

    /**
     * 设置部门
     *
     * @param sysDept 部门
     * @return 部门集合Small
     */
    private DeptSmallVo getDept(SysDept sysDept) {
        if (!ObjectUtils.isEmpty(sysDept)) {
            DeptSmallVo deptSmallVo = new DeptSmallVo();
            deptSmallVo.setId(sysDept.getId());
            deptSmallVo.setDeptName(sysDept.getDeptName());
            return deptSmallVo;
        }
        return null;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void delete(Set<String> ids) {
        if (userRepository.deleteAllByIdIn(ids) == 0) {
            throw new BaseException(Constant.DELETE_FAIL);
        }
    }

    @Override
    public List<UserSmallVo> userAll(String typeId, Integer type, Integer pageNum, Integer pageSize) {
        if (type != null && StringUtils.hasText(typeId) && decideType(type)) {
            //0部门，1角色，2岗位
            switch (type) {
                case 0:
                    SysDept sysDept = new SysDept();
                    sysDept.setId(typeId);
                    List<SysUser> usersByDept = userRepository.findAllByDept(sysDept);
                    return getUserSmallVos(usersByDept);
                case 1:
                    SysRole sysRole = new SysRole();
                    sysRole.setId(typeId);
                    List<SysUser> usersByRole = userRepository.findAllByRolesIn(Collections.singletonList(sysRole));
                    return getUserSmallVos(usersByRole);
                case 2:
                    SysPost sysPost = new SysPost();
                    sysPost.setId(typeId);
                    List<SysUser> usersBypost = userRepository.findAllByPostsIn(Collections.singletonList(sysPost));
                    return getUserSmallVos(usersBypost);
                default:
                    return null;
            }
        }
        List<SysUser> users = userRepository.findAll();
        return getUserSmallVos(users);
    }

    private Boolean decideType(Integer type) {
        Integer[] types = {0, 1, 2};
        for (Integer i : types) {
            if (i.equals(type)) {
                return true;
            }
        }
        return false;
    }

    private Integer[] decidePage(Integer pageNum, Integer pageSize) {
        Integer[] pages = new Integer[2];
        if (pageNum != null && pageSize != null) {
            pageNum = pageNum <= 0 ? 1 : pageNum;
            pageSize = pageSize <= 0 ? 10 : pageSize;
            pages[0] = pageNum;
            pages[1] = pageSize;
        }
        return pages;
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public void update(SysUser sysUser) {
        if (!StringUtils.hasText(sysUser.getId())) {
            throw new BaseException(Constant.ID_EXITS);
        }
        SysUser user = userRepository.findById(sysUser.getId()).orElseGet(SysUser::new);
        checkUser(sysUser, user);
        user.setUsername(sysUser.getUsername());
        user.setRealName(sysUser.getRealName());
        user.setNickname(sysUser.getNickname());
        user.setActive(sysUser.getActive());
        user.setAvatar(sysUser.getAvatar());
        user.setSort(sysUser.getSort());
        user.setStatus(sysUser.getStatus());
        user.setPhone(sysUser.getPhone());
        user.setEmail(sysUser.getEmail());
        user.setSex(sysUser.getSex());
        if (!CollectionUtils.isEmpty(sysUser.getRoles())) {
            user.setRoles(sysUser.getRoles());
        }
        if (!ObjectUtils.isEmpty(sysUser.getDept())) {
            user.setDept(sysUser.getDept());
        }
        user.setUpdateTime(new Date());
        userRepository.save(user);
    }

    private void checkUser(SysUser sysUser, SysUser user) {
        if (!ObjectUtils.isEmpty(user)) {
            if (!user.getId().equals(sysUser.getId())) {
                throw new BaseException(Constant.DATA_EXITS);
            }
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public int restPwd(List<RestPwd> restPwds) {
        if (CollectionUtils.isEmpty(restPwds)) {
            throw new BaseException("参数错误");
        }
        Set<SysUser> restListUser = new HashSet<>();
        restPwds.forEach(item -> {
            if (StringUtils.hasText(item.getId()) && StringUtils.hasText(item.getNewPwd())) {
                SysUser user = userRepository.findById(item.getId()).orElse(new SysUser());
                user.setPassword(item.getNewPwd());
                restListUser.add(user);
            } else {
                throw new BaseException("ID和新密码不能为空");
            }
        });
        if (!CollectionUtils.isEmpty(restListUser)) {
            List<SysUser> sysUsers = userRepository.saveAll(restListUser);
            return sysUsers.size();
        }
        return 0;
    }

    @Override
    public List<SysUser> findUserByRoleIds(Set<String> roleIds) {
        List<SysRole> sysRolesByIds = new ArrayList<>();
        for (String roleId : roleIds) {
            SysRole sysRole = new SysRole();
            sysRole.setId(roleId);
            sysRolesByIds.add(sysRole);
        }
        return userRepository.findAllByRolesIn(sysRolesByIds);
    }

    @Override
    public List<SysUser> findUserByPostIds(Set<String> roleIds) {
        List<SysPost> sysPostsByIds = roleIds.stream().map(postId -> {
            SysPost sysPost = new SysPost();
            sysPost.setId(postId);
            return sysPost;
        }).collect(Collectors.toList());
        return userRepository.findAllByPostsIn(sysPostsByIds);
    }

    @Override
    public void updateInfo(SysUser sysUser) {
        if (!StringUtils.hasText(sysUser.getId())) {
            throw new BaseException(Constant.ID_EXITS);
        }
        SysUser user = userRepository.findById(sysUser.getId()).orElseGet(SysUser::new);
        checkUser(sysUser, user);
        user.setRealName(sysUser.getRealName());
        user.setNickname(sysUser.getNickname());
        user.setEmail(sysUser.getEmail());
        user.setAbout(sysUser.getAbout());
        user.setBirthday(sysUser.getBirthday());
        user.setPhone(sysUser.getPhone());
        user.setSex(sysUser.getSex());
        user.setLocation(sysUser.getLocation());
        user.setRealName(sysUser.getRealName());
        user.setUpdateTime(new Date());
        userRepository.save(user);
    }

    @Override
    public void loginOut() {
        String username = ServletUtils.getUsername();
        String userId = ServletUtils.getUserId();
        redisUtils.clearRedis(RedisUtils.clearType.USER_AUTH,userId);
        redisUtils.clearRedis(RedisUtils.clearType.USER_INFO,userId);
    }

    @Override
    public SysUser loadUserByUsername(String username) {
        return userRepository.findByUsername(username);
    }

    @Override
    public void password(PwdDto pwdDto) {
        String userId = !StringUtils.hasText(pwdDto.getUserId()) ? ServletUtils.getUserId() : pwdDto.getUserId();
        SysUser sysUser = userRepository.findById(userId).orElseGet(SysUser::new);
        if (sysUser.getId()==null && !sysUser.getId().equals(userId)){
            throw new BaseException("用户不存在，密码修改失败");
        }
        if (!pwdDto.getOldPass().equals(sysUser.getPassword())){
            throw new BaseException("旧密码不正确");
        }
        if (!pwdDto.getNewPass().equals(pwdDto.getCheckPass())){
            throw new BaseException("两次密码不一致");
        }
        sysUser.setPassword(pwdDto.getNewPass());
        userRepository.save(sysUser);
    }

    @Override
    public Set<SysRole> findRolesByUserId(String userId) {
        SysUser sysUser = userRepository.findById(userId).orElseGet(SysUser::new);
        return sysUser.getRoles();
    }

    @Override
    public void updateBatch(UserDto userDto) {
        List<SysUser> userList = userRepository.findAllByIdIn(userDto.getUserIds());
        if (ObjectUtils.isEmpty(userDto)) {
            throw new BaseException("参数错误");
        }
        //0部门，1角色，2岗位
        switch (userDto.getType()){
            case 0:
                SysDept sysDept = new SysDept();
                sysDept.setId(userDto.getTypeId());
                userList.forEach(sysUser -> sysUser.setDept(sysDept));
                List<SysUser> sysUsers0 = userRepository.saveAll(userList);
                if (CollectionUtils.isEmpty(sysUsers0)) {
                    throw new BaseException("修改失败");
                }
                break;
            case 1:
                SysRole sysRole = new SysRole();
                sysRole.setId(userDto.getTypeId());
                userList.forEach(sysUser -> sysUser.getRoles().add(sysRole));
                List<SysUser> sysUsers1 = userRepository.saveAll(userList);
                if (CollectionUtils.isEmpty(sysUsers1)) {
                    throw new BaseException("修改失败");
                }
                break;
            case 2:
                SysPost sysPost = new SysPost();
                sysPost.setId(userDto.getTypeId());
                userList.forEach(sysUser -> sysUser.getPosts().add(sysPost));
                List<SysUser> sysUsers2 = userRepository.saveAll(userList);
                if (CollectionUtils.isEmpty(sysUsers2)) {
                    throw new BaseException("修改失败");
                }
                break;
            default:
                throw new BaseException("参数类型错误");
        }

    }


    public List<UserSmallVo> findAllByDeptIds(String deptId) {
        if (StringUtils.hasText(deptId)) {
            SysDept sysDept = new SysDept();
            sysDept.setId(deptId);
            List<SysUser> userList = userRepository.findAllByDept(sysDept);
            List<UserSmallVo> smallVoList = getUserSmallVos(userList);
            return smallVoList;
        }
        return null;
    }

    /**
     * 转换实体
     *
     * @param sysUsers 用户数据
     * @return 简单集合数据
     */
    private List<UserSmallVo> getUserSmallVos(List<SysUser> sysUsers) {
        List<UserSmallVo> smallVoList = sysUsers.stream().map(sysUser -> {
            UserSmallVo vo = new UserSmallVo();
            BeanUtils.copyProperties(sysUser, vo);
            return vo;
        }).collect(Collectors.toList());
        return smallVoList;
    }
}
